package com.hubiao.basics.websocket;

import lombok.extern.log4j.Log4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Description
 *
 * @author hubiao
 * @since 2021-01-26 21:18
 */

@Log4j
@ServerEndpoint("/websocket/{uid}")
@Component
public class WebSocketServer {

    //有时候会因为初始化类属性时容器中还未有相应的been造成创建WebSocketServer been失败，可以考虑将下边的两个属性在要使用的地方获取使用
   /* private static SysUserDepartMapper sysUserDepartMapper= SpringUtil.getBean(SysUserDepartMapper.class);
    private static SysUserRoleMapper sysUserRoleMapper= SpringUtil.getBean(SysUserRoleMapper.class);*/

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    //在真正的使用中这个set的大小回应为非正规的退出（没调用close方法就关闭页面了），导致数量只增不减，后期需要一个客户端定期的一个心跳机制来控制删除一些已死的连接
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    //用户id
    private String uid = "";
    //用户数所属部门
    private String depid = "";
    //用户所属部门
    private String roleid = "";

    /**
     * 发送消息到用户
     */
    public static void sendInfoToUser(String message, @PathParam("uid") String uid) throws IOException {
        log.info( "推送消息到窗口" + uid + "，推送内容:" + message );
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的，为null则全部推送
                if (uid == null) {
                    item.sendMessage( message );
                } else if (item.uid.equals( uid )) {
                    item.sendMessage( message );
                }
            } catch (IOException e) {
                continue;
            }
        }
        log.info( "推送完毕" );
    }

    /**
     * 发送消息到部门
     */
    public static void sendInfoToDep(String message, @PathParam("depid") String depid) throws IOException {
        log.info( "推送消息到窗口" + depid + "，推送内容:" + message );
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的，为null则全部推送
                if (depid == null) {
                    item.sendMessage( message );
                } else if (item.depid.equals( depid )) {
                    item.sendMessage( message );
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    /**
     * 发送消息到角色
     */
    public static void sendInfoToRole(String message, @PathParam("roleid") String roleid) throws IOException {
        log.info( "推送消息到窗口" + roleid + "，推送内容:" + message );
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的，为null则全部推送
                if (roleid == null) {
                    item.sendMessage( message );
                } else if (item.roleid.equals( roleid )) {
                    item.sendMessage( message );
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("uid") String uid) {
        //根据用户id获取部门
       /* List<SysUserDepart> deps = sysUserDepartMapper.getUserDepartByUid(uid);
        //根据用户id获取角色
        List<String> roles = sysUserRoleMapper.getRoleIdByUserId(uid);*/
        this.session = session;
        webSocketSet.add( this );     //加入set中
        addOnlineCount();           //在线数加1
        log.info( "有新窗口开始监听:" + uid + ",当前在线人数为" + getOnlineCount() + "|" + webSocketSet.size() );
        this.uid = uid;
        /*if (deps!=null&&deps.size()>0) {
            this.depid = deps.get(0).getDepId();
        }
        if (roles!=null&&roles.size()>0) {
            this.roleid = roles.get(0);
        }*/
        try {
            sendMessage( "连接成功" );
        } catch (IOException e) {
            log.error( "websocket IO异常" );
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove( this );  //从set中删除
        subOnlineCount();           //在线数减1
        log.info( "有一连接关闭！当前在线人数为" + getOnlineCount() );
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info( "收到来自窗口" + uid + "的信息:" + message );
        //群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage( message );
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error( "发生错误" );
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText( message );
    }

}
